class Axios {
    constructor() {
        // 实现axios.interceptors.response.use和axios.interceptors.request.use
        this.interceptors = {
            // 请求拦截器
            request: new InterceptorsManage(),
            // 响应拦截器
            response: new InterceptorsManage()
        }
    }

    request(config) {
        // 使用拦截器请求和组装队列
        let chain = [this.sendAjax.bind(this), null]

        // 将请求拦截加入
        this.interceptors.request.handlers.forEach(item => {
            // 放到最前面
            chain.unshift(item.resolve, item.reject);
        })
        //  响应拦截器
        this.interceptors.response.handlers.forEach(item => {
            chain.push(item.resolve, item.reject);
        })
        // chains大概是['fulfilled1','reject1','fulfilled2','reject2','this.sendAjax','undefined','fulfilled2','reject2','fulfilled1','reject1']这种形式
        // 执行队列
        let promise = Promise.resolve(config);
        while (chain.length > 0) {
            promise = promise.then(chain.shift(), chan.shift());
        }
        return promise;
    }

    sendAjax(config) {
        return new Promise(res => {
            const { url = '', method = 'get', param = '', data = {} } = config;
            const xhr = new XMLHttpRequest();
            xhr.open(method, url, true);
            xhr.onload = function () {
                console.log(xhr.responseText);
                res(xhr.responseText);
            }

            xhr.send(data);
        })
    }
}


/**
 * 导出axios
 * 当使用 axios.get('/user?ID=12345')的时候实际上使用的就是request方法
 * @returns axios实例
 */
function createAxios() {
    let axios = new Axios();
    const req = axios.request.bind(axios);

    // 将原型上的方法，搬运到request上。为什么这么做？因为我们要实现axios.get({})...
    utils.extend(req, Axios.prototype, axios)
    // 将intercepter也都挂到request上
    utils.extend(req, axios);
    return req;
}


// 测试request请求
let axios = createAxios();
axios({
    url: 'http://localhost:3000/apollo',
    method: 'get'
}).then(res => {
    console.log(res);
});



/***********实现axios.method的方式（）请求*************/
const methodArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
const paramTWO = ['get', 'delete', 'head', 'options'];
// 定义方法挂载在原型上
methodArr.forEach(met => {
    Axios.prototype[met] = function () {
        console.log(`执行${met}方法`);
        let config;
        // 2个参数的
        if (paramTWO.includes(met)) {
            config = {
                method: met,
                url: arguments[0],
                // 只取后面一个参数
                ...arguments[1] || {}
            }
        } else {
            config = {
                method: met,
                url: arguments[0],
                data: arguments[1] || {},
                ...arguments[2] || {}
            }
        }
        return this.request(config)
    }
})


const utils = {
    // 工具类，将B的方法混入到A 并且改变this指向
    extend(dest, orig, context) {
        if (typeof dest != 'object' || typeof orig != 'object') {
            return;
        }
        for (const key in orig) {
            if (orig.hasOwnProperty(key)) {
                const element = orig[key];
                // 判断一下是不是函数
                if (typeof element == 'function') {
                    dest[key] = element.bind(context);
                } else {
                    dest[key] = element;
                }
            }
        }
    }
}



// 构造拦截器函数
class InterceptorsManage {
    constructor() {
        this.handlers = [];
    }

    use(resolve, reject) {
        this.handlers.push({ resolve, reject })
    }
}